package edu.pku.sei.ocl.visitor.impl;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreEList;
import org.eclipse.emf.ecore.util.EcoreUtil;

import edu.pku.sei.ocl.cst.FeatureCallExpCS;
import edu.pku.sei.ocl.cst.IteratorExpCS;
import edu.pku.sei.ocl.cst.OCLExpressionCS;
import edu.pku.sei.ocl.cst.OperationCallExpCS;
import edu.pku.sei.ocl.cst.ReflectiveFeatureCallExpCS;
import edu.pku.sei.ocl.cst.VariableExpCS;
import edu.pku.sei.ocl.evaluation.Context;
import edu.pku.sei.ocl.evaluation.Environment;
import edu.pku.sei.ocl.evaluation.OCLVariable;
import edu.pku.sei.ocl.utilities.OCLConstant;
import edu.pku.sei.ocl.utilities.OCLStandardLibrary;
import edu.pku.sei.ocl.value.OCLBasicCollection;
import edu.pku.sei.ocl.value.OCLCollection;
import edu.pku.sei.ocl.value.OCLEcoreCollection;

public class ExpressionEnforceVisitor extends ModelEnforceVisitor {
//	static {
//		expressionEnforce = new ExpressionEnforceVisitor();
//	}

	@Override
	protected boolean setModelReference(EObject objA, EReference feature,
			Object object) {
		// TODO Auto-generated method stub
		if(feature.isChangeable()==false)
			return false;
		if(feature.isMany())
			return false;
		
		EObject objB = (EObject)object;
		EObject oldContainerB = objB.eContainer();	
		EReference oldReferenceB = objB.eContainmentFeature();
		
		EObject oldTargetA = (EObject) objA.eGet(feature);
		if(oldTargetA==objB)
			return false;
		
		boolean deleteOldReferenceA = false;
		boolean deleteOldReferenceB = false;
		
		if(oldTargetA!=null){
			deleteOldReferenceA = true;
		}
		
		if(feature.isContainment()&&oldContainerB!=null&&oldReferenceB!=null){
			deleteOldReferenceB = true;
		}
		
		if(deleteOldReferenceA){
			if(feature.isContainment()){
				deleteObjectFromModel(oldTargetA);
			} else {
				removeLink(objA, feature, oldTargetA);
			}
		}
		
		if(deleteOldReferenceB){
			removeLink(oldContainerB, oldReferenceB, objB);
		}
		
		objA.eSet(feature, objB);
		Environment.log.create(objA, objB, feature);
		
		return true;
	}

	@Override
	protected boolean removeLink(EObject objA, EReference feature,
			EObject oldTargetA) {
		// TODO Auto-generated method stub
		if(objA==null)
			return false;
		EcoreUtil.remove(objA, feature, oldTargetA);
		Environment.log.delete(objA, oldTargetA, feature);
		return true;
	}

	@Override
	protected boolean deleteObjectFromModel(EObject obj) {
		EObject container = obj.eContainer();
		if(container!=null){
			EReference feature = obj.eContainmentFeature();
			Environment.log.delete(container, obj, feature);
		}
		EcoreUtil.delete(obj);		
		Environment.log.delete(obj);
		System.out.println("delete object from model");
		return true;
	}

	@Override
	protected boolean setModelAttribute(EObject eobj, EAttribute feature,
			Object newValue) {
		Object oldValue = eobj.eGet(feature);
		if(feature.isChangeable()){
			Object unwrap = OCLStandardLibrary.unwrap(newValue);
			eobj.eSet(feature, unwrap);
			if((oldValue !=null && !oldValue.equals(unwrap))
					||(unwrap !=null && !unwrap.equals(oldValue)))
				Environment.log.change(eobj, (EAttribute)feature, oldValue);
			return true;
		}
		return false;
	}
	
	@Override
	public Object visitVariableExpCS(VariableExpCS expr, Context context,
			Object... expectedResult) {

		if(expectedResult[0]==OCLConstant.OCLInvalid)
			return super.visitVariableExpCS(expr, context, expectedResult);
		
		OCLVariable var = context.getVariable(expr.getSimpleNameCS().getValue());
		if(var==null){
			System.out.println("visitVariableExpCS: no variable founded");
			return new Object[]{OCLConstant.OCLInvalid,context};
		}
		
		Object oldValue = context.getValue(var);

		{
			expectedResult[0] = OCLStandardLibrary.castValue(expectedResult[0],var.getType());
			
			if(expectedResult[0].equals(oldValue)==false){
				Context nc = context.getCopy();
				nc.putValue(var, expectedResult[0]);
				return new Object[]{expectedResult[0],nc};
			}
		}
		return new Object[]{oldValue,context};
	}

	@Override
	protected Object enforceForAllExp(IteratorExpCS expr,
			Object[] currentState, OCLVariable v1, OCLVariable v2,
			OCLExpressionCS body, OCLCollection sourceCol, Context c,
			Object... expectedResult) {
		// TODO Auto-generated method stub
		return enforceForEachExp(expr, currentState, v1, v2, body, sourceCol, c,
				expectedResult);
	}

	@Override
	protected Object enforceAndExp(OperationCallExpCS expr, Context context,
			Object... expectedResult) {
		// TODO Auto-generated method stub
		return enforceLAndExp(expr, context, expectedResult);
	}

	@Override
	protected Object enforceOrExp(OperationCallExpCS expr, Context context,
			Object... expectedResult) {
		// TODO Auto-generated method stub
		return enforceLOrExp(expr, context, expectedResult);
	}
	
	
}
